package wxdb.index.btree;

import java.io.Serializable;

import wxdb.query.Constant;
import wxdb.query.IntConstant;
import wxdb.query.StringConstant;
import wxdb.record.Schema;
import wxdb.record.TableInfo;
import wxdb.tx.Transaction;
import wxdb.ui.Global;
import wxdb.record.RID;
import static java.sql.Types.*;

public class BTreePage implements Serializable{
	
	private TableInfo ti;
	private int slotsize;
	private int pid;		//current page number
	
	public BTreePage(int pid, TableInfo ti){
		this.ti = ti;
		this.pid = pid;
		slotsize = ti.getRecordLen();
	}
	
	public int findFirstSlotBefore(Constant searchKey){
		int total = getTotalSlotNum();
		int slot = 0;
		while(slot < total){
			if(getFldVal(slot, "dataval").compareTo(searchKey) < 0){
				++slot;
				continue;
			}
			else
				break;
		}
		return slot-1;
	}
	
	public int split(int splitpos, int flag){
		int newPid = appendNewPage(flag);
		ti.addPage(pid, newPid);
		BTreePage newpage = new BTreePage(newPid, ti);
		transferSlots(splitpos, newpage);
		newpage.setFlag(flag);
		return newPid;
	}
	
	private int appendNewPage(int flag){
		BTreePageFormatter pf = new BTreePageFormatter(ti, flag);
		return Transaction.append(ti.getDbid(), pf);
	}
	
	private void transferSlots(int startslot, BTreePage dest){
		int slotCount = 0;
		while(startslot < getTotalSlotNum()){
			dest.insert(slotCount);
			for(String fldName : ti.getSchema().getFields()){
				dest.setFldVal(slotCount, fldName, getFldVal(startslot, fldName));
			}
			delete(startslot);
			++slotCount;
		}
	}
	
	public void insert(int slot){
		int size = getTotalSlotNum();
		for(int count=size; count>slot; --count)
			copySlot(count-1, count);
		setTotalSlotNum(getTotalSlotNum()+1);
	}
	
	public void copySlot(int fromslot, int toslot){
		for(String fldName : ti.getSchema().getFields()){
			setFldVal(toslot, fldName, getFldVal(fromslot, fldName));
		}
	}
	
	public void delete(int slot){
		int size = getTotalSlotNum();
		for(++slot; slot<size; ++slot)
			copySlot(slot, slot-1);
		setTotalSlotNum(getTotalSlotNum()-1);
	}
	
	public void insertDir(int slot, Constant dataval, int pid){
		insert(slot);
		setFldVal(slot, "dataval", dataval);
		setFldVal(slot, "pid", new IntConstant(pid));
	}
	
	public void insertLeaf(int slot, Constant dataval, RID rid){
		insert(slot);
		setFldVal(slot, "dataval", dataval);
		setFldVal(slot, "pid", new IntConstant(rid.getPid()));
		setFldVal(slot, "slotnum", new IntConstant(rid.getSlotNum()));
	}
	
	public RID getDataRid(int slot){
		return new RID(getInt(slot, "pid"), getInt(slot, "slotnum"));
	}
	
	public int getFlag(){
		return Transaction.getInt(ti.getDbid(), pid, 0);
	}
	
	public void setFlag(int flag){
		Transaction.setInt(ti.getDbid(), pid, 0, flag);
	}
	
	public void setTotalSlotNum(int num){
		Transaction.setInt(ti.getDbid(), pid, Transaction.getIntSize(), num);
	}
	
	public boolean isFull(){
		return getSlotPos(0)+(getTotalSlotNum()+1)*slotsize >= Global.STORAGE_PAGE_SIZE;
	}
	
	public int getPid(int slot){
		return getInt(slot, "pid");
	}
	
	public void setFldVal(int slot, String fldName, Constant val){
		int offset = getSlotPos(slot) + ti.getOffset(fldName);
		if(ti.getSchema().getType(fldName) == INTEGER)
			Transaction.setInt(ti.getDbid(), pid, offset, (Integer)val.asJavaVal());
		else
			Transaction.setString(ti.getDbid(), pid, offset, (String)val.asJavaVal(), ti.getSchema().getLen(fldName));
	}
	
	public Constant getFldVal(int slot, String fldName){
		Schema sch = ti.getSchema();
		int type = sch.getType(fldName);
		if(type == INTEGER)
			return new IntConstant(getInt(slot, fldName));
		else
			return new StringConstant(getString(slot, fldName));
		
	}
	
	public int getCurrnetPid(){
		return pid;
	}
	
	private int getInt(int slot, String fldName){
		int offset = getSlotPos(slot) + ti.getOffset(fldName);
		return Transaction.getInt(ti.getDbid(), pid, offset);
	}
	
	private String getString(int slot, String fldName){
		int offset = getSlotPos(slot) + ti.getOffset(fldName);
		return Transaction.getString(ti.getDbid(), pid, offset);
	}
	
	public int getSlotPos(int slot){
		return 2*Transaction.getIntSize() + slot*slotsize;
	}
	
	public int getTotalSlotNum(){
		return Transaction.getInt(ti.getDbid(), pid, Transaction.getIntSize());
	}
}
